package com.example.demo.controller;

import com.example.demo.constant.Constants;
import com.example.demo.game.GameReadyResponse;
import com.example.demo.game.GameResponse;
import com.example.demo.mapper.UserMapper;
import com.example.demo.match.OnlineUserManager;
import com.example.demo.model.User;
import com.example.demo.room.Room;
import com.example.demo.room.RoomManager;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;

@Controller
@Slf4j
public class GameController extends TextWebSocketHandler {
    private ObjectMapper objectMapper=new ObjectMapper();
    @Autowired
    RoomManager roomManager=new RoomManager();
    @Autowired
    private OnlineUserManager onlineUserManager;
    @Autowired
    private UserMapper userMapper;
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        GameReadyResponse response=new GameReadyResponse();
        //1.首先还是从HTTP Session中获取用户的信息
        User user = (User) session.getAttributes().get(Constants.USER_SESSION_KEY);
        //判断当前用户并没有登录,而是直接访问了我们的游戏页面,并将结果返回给客户端.
        if (user==null){
            response.setOk(false);
            response.setReason("当前用户未登录");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
            return;
        }
        //2.判断当前玩家是不是已经进入了游戏房间.
        Room room = roomManager.getRoomByUserId(user.getUserId());

        if (room==null){
            response.setOk(false);
            response.setReason("用户尚未进入房间");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
            return;
        }
        //3.判断当前用户是不是多开.
        //前面在游戏大厅页面我们使用OnlineUserManager来表示当前游戏大厅中用户的在线状态
        //当玩家进入房间的时候,游戏大厅页面就已经被销毁了,玩家的信息也就没有了
        //所以我们要在游戏房间内重新获取玩家的在线状态


        if (onlineUserManager.getFromGameHall(user.getUserId())!=null ||
            onlineUserManager.getFromGameRoom(user.getUserId())!=null){
            //用户在游戏大厅上线并且在游戏房间内也是上线的情况下也视为多开
            response.setReason("当前用户重复登录");
            response.setOk(true);
            response.setMessage("repeatConnection");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
            return;
        }
        //4.设置上线
        onlineUserManager.enterGameRoom(user.getUserId(), session);
        log.info("玩家 {}  进入游戏房间",user.getUsername());

        //这里加锁是避免两个玩家同时发起请求
        //两个客户端都认为自己是第一个进入的玩家,就会修改第一个if
        //那么就永远执行不了第二个if,游戏就无法开始
        synchronized (room){
            //5.把两个玩家加入到我们房间里面
            if(room.getUser1()==null){
                room.setUser1(user);
                room.setWhiteUser(user.getUserId());
                return;
            }
            if (room.getUser2()==null){
                room.setUser2(user);
                noticeGameReady(room, room.getUser1(), room.getUser2());
                // 通知玩家2
                noticeGameReady(room, room.getUser2(), room.getUser1());
                return;
            }


            //6.如果当前房间已满
            response.setOk(false);
            response.setReason("当前房间已经满了");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        }

    }
    private void noticeGameReady(Room room, User thisUser, User thatUser) throws IOException {
        GameReadyResponse resp = new GameReadyResponse();
        resp.setMessage("gameReady");
        resp.setOk(true);
        resp.setReason("");
        resp.setRoomId(room.getRoomId());
        resp.setThisUserId(thisUser.getUserId());
        resp.setThatUserId(thatUser.getUserId());
        resp.setWhiteUser(room.getWhiteUser());
        // 把当前的响应数据传回给玩家.
        WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thisUser.getUserId());
        webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
    }




    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        //1.获取当前对象
        User user= (User) session.getAttributes().get(Constants.USER_SESSION_KEY);
        if (user==null){
            return;
        }
        //根据玩家id获取房间对象
        Room room = roomManager.getRoomByUserId(user.getUserId());
        //处理落子响应
        room.putChess(message.getPayload());
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        //1.获取当前对象
        User user= (User) session.getAttributes().get(Constants.USER_SESSION_KEY);


        //2.看当前玩家是否在房间内
        WebSocketSession exitSession = onlineUserManager.getFromGameRoom(user.getUserId());
        //3.如果当前会话和之前存储的是同一个会话我们再进行下线
        if (exitSession==session){
            onlineUserManager.exitGameRoom(user.getUserId());
            log.info("当前玩家 {}  离开房间",user.getUsername());
        }

        //掉线的处理,直接宣布对方获胜
        noticeThatUserWin(user);
    }



    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        //1.获取当前对象
        User user= (User) session.getAttributes().get(Constants.USER_SESSION_KEY);


            //2.看当前玩家是否在房间内
            WebSocketSession exitSession = onlineUserManager.getFromGameRoom(user.getUserId());
            //3.如果当前会话和之前存储的是同一个会话我们再进行下线
            if (exitSession==session){
                onlineUserManager.exitGameRoom(user.getUserId());
                log.info("当前玩家 {}  离开房间",user.getUsername());
            }

        //掉线的处理,直接宣布对方获胜
        noticeThatUserWin(user);

    }
    private void noticeThatUserWin(User user) throws IOException {
        // 1. 根据当前玩家, 找到玩家所在的房间
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if (room == null) {
            // 这个情况意味着房间已经被释放了, 也就没有 "对手" 了
            System.out.println("当前房间已经释放, 无需通知对手!");
            return;
        }

        // 2. 根据房间找到对手
        User thatUser = (user == room.getUser1()) ? room.getUser2() : room.getUser1();
        // 3. 找到对手的在线状态
        WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thatUser.getUserId());
        if (webSocketSession == null) {
            // 这就意味着对手也掉线了!
            System.out.println("对手也已经掉线了, 无需通知!");
            return;
        }
        // 4. 构造一个响应, 来通知对手, 你是获胜方
        GameResponse resp = new GameResponse();
        resp.setMessage("putChess");
        resp.setUserId(thatUser.getUserId());
        resp.setWinner(thatUser.getUserId());
        webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));

        // 5. 更新玩家的分数信息
        int winUserId = thatUser.getUserId();
        int loseUserId = user.getUserId();
        userMapper.userWin(winUserId);
        userMapper.userLose(loseUserId);

        // 6. 释放房间对象
        roomManager.remove(room.getRoomId(), room.getUser1().getUserId(), room.getUser2().getUserId());
    }
}
